#ifndef MINIMESH_H
#define MINIMESH_H

#include <deque>

#include "MiniObject.h"

namespace XYF
{
    /**
     * @file: MiniMesh.h
     * @author: xingyifan
     * @date: 2023-03-11 20:21
     *
     * @description: 前置声明结构
     */

    struct vertex3f;
    struct half_edge;
    struct face;

    /**
     * @file: MiniMesh.h
     * @author: xingyifan
     * @date: 2023-03-11 20:29
     *
     * @description: 顶点结构
     */

    struct vertex3f
    {
        Vector3f pos; // 坐标
        half_edge *e; // 以它为起点的一条半边
        int identity; // 顶点 id 标记
    };

    /**
     * @file: MiniMesh.h
     * @author: xingyifan
     * @date: 2023-03-11 20:30
     *
     * @description: 面结构
     */

    struct face
    {
        half_edge *e; // 面边界上的一条半边
        int identity; // 面标记
    };

    /**
     * @file: MiniMesh.h
     * @author: xingyifan
     * @date: 2023-03-11 20:30
     *
     * @description: 半边结构
     */

    struct half_edge
    {
        vertex3f *v;     // 起点
        half_edge *twin; // 反向边，不一定存在
        face *incFace;   // 沿着半边方向左边的面，这个面一定存在
        half_edge *prev; // 上一条半边，一定存在，在面上
        half_edge *next; // 下一条半边，一定存在，在面上
        int identity;    // 半边标记
    };

    /**
     * @file: MiniMesh.h
     * @author: xingyifan
     * @date: 2023-03-11 17:01
     *
     * @description: 材质结构体
     */

    struct material
    {
        float alpha; // 镜面反射指数
        float R[9];  // 反射率矩阵 a d s ：环境光、漫反射、镜面反射
    };

    /**
     * @file: MiniMesh.h
     * @author: xingyifan
     * @date: 2023-03-11 17:01
     *
     * @description: 多边形网格
     */

    class MiniMesh : public MiniObject
    {
    public:
        /**
         * @file MiniMesh.h
         * @author xingyifan
         * @date 2023-03-11 20:35
         *
         * @description: 网格类型
         */

        enum class MeshType
        {
            triangle,
            quadrilateral,
            pentagon,
            mixed
        };

        /**
         * @file MiniMesh.h
         * @author xingyifan
         * @date 2023-03-11 20:37
         *
         * @description: 元素类型
         */

        enum class Element
        {
            vertex,
            half_edge,
            face
        };

    public:
        MiniMesh(MeshType type);
        ~MiniMesh();

        // 重载返回类型
        virtual int GetType() override;

        /**
         * @file MiniMesh.h
         * @author xingyifan
         * @date 2023-03-11 20:34
         *
         * @description: 初始化构造操作
         */

        vertex3f *CreateVertex(Vector3f p, bool testSame = false);                                      // 创建顶点，并返回指针（可选检查顶点是否重合，如果重合，将返回重合顶点）
        face *CreateFace(vertex3f *v0, vertex3f *v1, vertex3f *v2, bool autoTwin = true);               // 创建三角面并返回，默认自动判定衔接相邻面
        face *CreateFace(vertex3f *v0, vertex3f *v1, vertex3f *v2, vertex3f *v3, bool autoTwin = true); // 创建四边形面，默认自动判定衔接相邻面
        face *CreateFace(vertex3f **vList, int n, bool autoTwin = true);                                // 创建多边形面，默认自动判定衔接相邻面
        void CreateTwin(face *f);                                                                       // 为一个面创建连接，会选择面的顶点相邻的面尝试建立连接

        /**
         * @file MiniMesh.h
         * @author xingyifan
         * @date 2023-03-11 20:36
         *
         * @description: 获取基本网格信息
         */

        int VertexNum() const;
        int FaceNum() const;
        int HalfEdgeNum() const;

        /**
         * @file MiniMesh.h
         * @author xingyifan
         * @date 2022-11-25 13:16
         *
         * @description: 结构删除操作
         */

        bool DeleteVertex(vertex3f *v);       // 删除顶点和相邻的面及其半边
        bool DeleteHalfEdge(half_edge *e);    // 删除半边和相邻的面，将目标顶点合并到起点，目标顶点将会孤立
        bool DeleteFace(face *f);             // 删除面和对应的半边
        bool DeleteIsolated(Element GetType); // 清除孤立元素

        /**
         * @file MiniMesh.h
         * @author xingyifan
         * @date 2022-12-15 14:07
         *
         * @description: 附加判断
         */

        bool HasVertexNormal();    // 是否有顶点法向
        bool IsConvexHull();       // 判断网格是否是凸包
        bool IsInMesh(Vector3f p); // 判断点是否在网格包围中

        /**
         * @file MiniMesh.h
         * @author xingyifan
         * @date 2022-12-15 13:27
         *
         * @description: 网格结构方法
         */

        bool StitchFace(face *f1, face *f2);                            // “缝合”两个面，返回是否出错
        bool OnFace(Vector3f p, face *f) const;                         // 判断一个点是否在面上
        bool AboveFace(Vector3f p, face *f) const;                      // 判断一个点是否在面的上方
        Vector3f Gouraud(vertex3f *v) const;                            // Gouraud 插值：计算顶点的平均法向
        Vector3f FaceNormal(face *f) const;                             // 计算单位面法向
        vertex3f *Destination(half_edge *e) const;                      // 获得边的目标点
        std::deque<vertex3f *> VertexOnFace(face *f) const;             // 获得一个面上的所有顶点
        std::deque<half_edge *> HalfEdgeOnFace(face *f) const;          // 获得一个面上的所有半边
        std::deque<half_edge *> CommonOnFace(face *f0, face *f1) const; // 获取两个面的公共半边
        std::deque<vertex3f *> AdjacentVertex(vertex3f *v) const;       // 获得 v 相邻的所有顶点
        std::deque<half_edge *> AdjacentHalfEdge(vertex3f *v) const;    // 获得以 v 为起点的半边
        std::deque<face *> AdjacentFace(vertex3f *v) const;             // 获得 v 相邻的所有面
        std::deque<face *> AdjacentFace(face *f) const;                 // 获得 f 相邻的所有面
        std::deque<vertex3f *> MeshProject(Vector3f pv) const;          // 取网格点沿着 pv 方向的投影多边形顶点
        std::deque<face *> TriangulateFace(face *f);                    // 将网格面三角化，返回三角化后的面列表

        /**
         * @file MiniMesh.h
         * @author xingyifan
         * @date 2023-01-10 20:56
         *
         * @description: 网格属性设置
         */

        void SetMaterial(float M[], float alpha); // 设置表面系数：反射系数和镜面反射系数

        /**
         * @file MiniMesh.h
         * @author xingyifan
         * @date 2022-12-25 19:33
         *
         * @description: 保存和读取操作
         */

        bool SaveMesh(const std::string &filename);                        // 保存网格顶点、面和邻面关系
        bool LoadMesh(const std::string &filename, bool testSame = false); // 加载网格，可选是否排除重合顶点

    public:
        /**
         * @file MiniMesh.h
         * @author xingyifan
         * @date 2022-12-15 13:55
         *
         * @description: 基本数据结构
         */

        std::deque<vertex3f *> m_vList;  // 顶点列表
        std::deque<half_edge *> m_eList; // 半边列表
        std::deque<face *> m_fList;      // 面列表
        std::deque<Vector3f> m_nList;    // 法向列表

        /**
         * @file MiniMesh.h
         * @author xingyifan
         * @date 2023-01-10 20:51
         *
         * @description: 进一步的网格属性
         */

        std::string m_name;  // 网格名
        MeshType m_type;     // 网格类型
        material m_material; // 网格材质
    };

    /**
     * @file MiniMesh.h
     * @author xingyifan
     * @date 2022-12-26 11:41
     *
     * @description: 常见曲面生成
     */

    bool sphere(MiniMesh *mesh, Vector3f center, Vector3f axis, Real a, Real b, Real c, int div = 20);           // 球面：中心位置、中轴、三个半长轴，分割份数
    bool cylinder(MiniMesh *mesh, Vector3f center, Vector3f axis, Real r, Real h, int divr = 40, int divh = 40); // 柱面：中心位置，中轴，半径，高度，分割份数
    bool cone(MiniMesh *mesh, Vector3f origin, Vector3f axis, Real theta, Real h, int divr = 40, int divh = 40); // 锥面：中心位置，中轴，半角，高度，分割份数
    bool ring(MiniMesh *mesh, Vector3f center, Vector3f axis, Real r, Real R, int divR = 40, int divr = 40);     // 圆环面：中心位置，垂直轴，内径，外径，圆的份数，每个圆有多少个点
}

#endif // MINIMESH_H
